#include <compilation/task/DominationTask.h>


namespace compilation
{


VirtualInstruction*
DominationTask::findDominatorWith(
    VirtualInstruction &source) const
{
  VirtualInstruction
      *thisInstruction   = m_previous,
      *sourceInstruction = &source;

  int thisHeight = m_height;
  int sourceHeight = sourceInstruction->getDominationHeight();

  // While not a common ancestor.
  while (thisInstruction != sourceInstruction)
  {
    // Set at the same height.
    while (thisHeight != sourceHeight)
    {
      while (thisHeight > sourceHeight)
      {
        assert(thisInstruction);

        if (thisInstruction->countPreviouses() == 1)
        {
          // If thisInstruction is not a label.
          // Jump to the previous.
          thisInstruction = thisInstruction->getSPreviousInstruction();
        }
        else
        {
          // If thisInstruction is a label.
          // Jump to the dominator.
          thisInstruction =
              instruction_cast<TLabel>(thisInstruction)->getSDominator();
        }
        thisHeight = thisInstruction->getDominationHeight();
      }

      while (sourceHeight > thisHeight)
      {
        assert(sourceInstruction);
        sourceInstruction =
            (sourceInstruction->countPreviouses() == 1)
            ? sourceInstruction->getSPreviousInstruction()
            : instruction_cast<TLabel>(sourceInstruction)->getSDominator();
        sourceHeight = sourceInstruction->getDominationHeight();
      }
    }

    if (thisInstruction == sourceInstruction) break;

    // Progress in parallel.
    {
      assert(thisInstruction && sourceInstruction);

      thisInstruction =
          (thisInstruction->countPreviouses() == 1)
          ? thisInstruction->getSPreviousInstruction()
          : instruction_cast<TLabel>(thisInstruction)->getSDominator();

      thisHeight = thisInstruction->getDominationHeight();

      sourceInstruction =
          (sourceInstruction->countPreviouses() == 1)
          ? sourceInstruction->getSPreviousInstruction()
          : instruction_cast<TLabel>(sourceInstruction)->getSDominator();

      sourceHeight = sourceInstruction->getDominationHeight();
    }
  }

  return thisInstruction;
}


void
DominationTask::applyOn(VirtualInstruction &instruction, VirtualAgenda&)
{
  instruction.setDominationHeight(m_height);

  m_previous = &instruction;
  ++m_height;
}


VirtualTask*
DominationTask::clone(void) const
{ return new DominationTask(*this); }


bool
DominationTask::mergeWith(VirtualTask &vtSource)
{
  DominationTask& source1 = vtSource.as<DominationTask>();

  m_previous = findDominatorWith(*source1.m_previous);
  m_height   = m_previous->getDominationHeight() + 1;

  return true;
}


} // compilation
